﻿using ProtoBuf.Meta;
using SyncCore.Sync.Models;
using System.IO.MemoryMappedFiles;

namespace SyncCore.Sync
{
    ///<summary>内存拷贝模块</summary>
    public sealed class PeerInMemoryShared : IDisposable
    {
        #region consts

        private const string SyncSharedMemoryMutexName = "SYNCSHAREDMEMORYMUTEX12w4";
        private const string SharedMemoryFileName = "SHAREDMEMORYFILENAME2145ewafsxda";
        private const long SharedMemorySize = 2 * 1024;

        #endregion

        #region props

        public bool IsMajorListenPeerInCurrentMachine { get; set; }

        public ClientHallSummaryInfo ClientHallSummaryInfo { get; private set; }

        #endregion

        #region fields

        ///<summary>
        /// 分享更新模式到同一台机器上。
        /// 因为绑定广播的监听端口只能有一个，但是一台机器可以开多个进程
        /// 如果不做处理，只有一个进程可以发现其它机器的活动
        /// 这里我们希望通过共享内存实现监听进程转播到其它进程。
        /// 这存在一个问题，就是当监听节点挂掉后，应该有其它节点升级成为监听节点。
        /// 成为监听节点的过程需要保证进程安全。
        ///</summary>
        private MemoryMappedFile _memoryMappedFile; // 共享内存内存
        private Stream _sharedMemoryStream; // 共享内存。
        private Mutex _sharedMemoryMutex; // 共享内存的锁。
        private Task _updateSharedMemoryThread; // 更新共享内存部分的线程。
        private RuntimeTypeModel _clientHallSummaryInfoTypeModel;
        private CancellationTokenSource _cancellationTokenSource;
        private AddressPortStateList _addressPortStateList;

        #endregion

        #region ctors

        public PeerInMemoryShared(CancellationTokenSource cancellationTokenSource, AddressPortStateList addressPortStateList)
        {
            _cancellationTokenSource = cancellationTokenSource;
            _addressPortStateList = addressPortStateList;
        }

        #endregion

        #region methods


        ///<summary>初始化内存分享部分。</summary>
        public void InitMemoryShared()
        {
            InitSharedMemoryStreamMutex();

            _clientHallSummaryInfoTypeModel = RuntimeTypeModel.Create();
            _clientHallSummaryInfoTypeModel.Add<ClientHallSummaryInfo>();

            _memoryMappedFile = MemoryMappedFile.CreateOrOpen(SharedMemoryFileName, SharedMemorySize);
            _sharedMemoryStream = _memoryMappedFile.CreateViewStream();
            _updateSharedMemoryThread = new Task(UpdateSharedMemoryEntry);
        }

        public void StartMemoryShared()
        {
            _updateSharedMemoryThread.Start();
        }

        ///<summary>初始化共享内存时的锁。</summary>
        private void InitSharedMemoryStreamMutex()
        {
            _sharedMemoryMutex = new Mutex(false, SyncSharedMemoryMutexName, out var isCreate);
        }

        ///<summary>回收内存分享部分。</summary>
        private void DisposeMemoryShared()
        {
            _sharedMemoryStream.Close();
            _sharedMemoryStream.Dispose();

            _sharedMemoryStream.Close();
            _sharedMemoryStream.Dispose();

            _sharedMemoryMutex.Close();
            _sharedMemoryMutex.Dispose();

        }

        private void UpdateSharedMemoryEntry()
        {
            while (_cancellationTokenSource.IsCancellationRequested == false)
            {
                UpdateSharedMemory();
                Thread.Sleep(1000);
            }
        }


        private void UpdateSharedMemory()
        {
            if (IsMajorListenPeerInCurrentMachine)
            {
                UpdateSharedMemoryForMajorListenPeer();
            }
            else
            {
                UpdateSharedMemoryForOtherPeerInMachine();
            }
        }

        ///<summary>主监听需要进行信息整合，并创建视图。</summary>
        private void UpdateSharedMemoryForMajorListenPeer()
        {
            var clientHallSummaryInfo = _addressPortStateList.GetClientHallSummaryInfo();
            ClientHallSummaryInfo = clientHallSummaryInfo;

            var stream = _sharedMemoryStream;

            _sharedMemoryMutex.WaitOne();

            stream.Seek(0, SeekOrigin.Begin);
            _clientHallSummaryInfoTypeModel.SerializeWithLengthPrefix(stream, clientHallSummaryInfo, typeof(ClientHallSummaryInfo), ProtoBuf.PrefixStyle.Base128, 2);

            _sharedMemoryMutex.ReleaseMutex();

        }

        ///<summary>被动监听</summary>
        private void UpdateSharedMemoryForOtherPeerInMachine()
        {
            var stream = _sharedMemoryStream;

            _sharedMemoryMutex.WaitOne();

            stream.Seek(0, SeekOrigin.Begin);
            try
            {
                ClientHallSummaryInfo = _clientHallSummaryInfoTypeModel.DeserializeWithLengthPrefix(stream, null, typeof(ClientHallSummaryInfo), ProtoBuf.PrefixStyle.Base128, 2) as ClientHallSummaryInfo;

            }
            catch (Exception ex)
            {
            }
          
            _sharedMemoryMutex.ReleaseMutex();

        }

        public void Dispose()
        {
            DisposeMemoryShared();
        }


        #endregion

    }

    public partial class Peer
    {
        #region fields

        private PeerInMemoryShared _peerInMemoryShared;

        #endregion

        ///<summary>初始化内存分享部分。</summary>
        private void InitMemoryShared(AddressPortStateList addressPortStateList)
        {
            _peerInMemoryShared = new PeerInMemoryShared(_cancellationTokenSource, addressPortStateList);
            _peerInMemoryShared.InitMemoryShared();
            _peerInMemoryShared.IsMajorListenPeerInCurrentMachine = _isMajorListenPeerInCurrentMachine;
        }

        private void StartMemoryShared()
        {
            _peerInMemoryShared.StartMemoryShared();
        }

        ///<summary>回收内存分享部分。</summary>
        private void DisposeMemoryShared()
        {
            _peerInMemoryShared.Dispose();
        }

    }
}
